디즈니 캐릭터 분류 모델
import fastai
print(fastai.__version__)
fastbook 제공, 유틸리티 라이브러리 설치
마이크로소프트 Bing 검색엔진을 통한 이미지 검색 등의 라이브러리는 fastai 차원에서 제공하는 일반화된 라이브러리가 아님. 다만, 책 내용의 실습을 위해서 fastbook 저장소에서 별도로 작성되어 제공되는것임.
만약, 책 실습 외의 상황에서도 search_images_bing 등의 API를 사용하고 싶다면, 반드시 아래 코드를 실행하여 fastbook 패키지를 import 해 줘야함.
fastbook의 유틸리티 함수로서 작성된 함수의 목록은 다음과 같음.
search_images_bingplot_functiondraw_treecluster_columns
딱히 구현상 어려운 수준의 함수는 아니므로, 참고하여 직접 구현해도 됨.
!pip install -Uqq fastbook
import fastbook
from fastbook import *
from fastai.vision.all import *
from fastai.vision.widgets import *
Azure Cognitive Service API 키 값
API 키를 얻어오기 위해서는
일단 MS Azure에 가입이 되어 있어야함
- 계정이 없다면 가입 링크에 접속한 후, "체험 계정 만들기" 버튼을 클릭해서 계정을 생성해야함
- 계정이 있다면, 단순히 로그인만 해 주면 됨
MS Azure 계정이 있다면, 로그인 후 Azure Portal에 접속해 줘야함
- 상단의 검색 바에서 "Cognitive Services"를 타이핑한 다음, 검색된 결과를 클릭함 (당연히 Cognitive Services 라는걸 클릭 해야함)
- +New 버튼을 클릭
- Marketplace에서, "Bing Search"를 검색하여 클릭
- "Create" 버튼을 클릭
- 각종 정보 입력. 단, "Pricing Tier"에는 반드시 "무료인 것을 선택"
- Resource Group이 없는 경우, 텍스트박스 하단의 "New Group"을 클릭하여 하나 생성
생성된 Bing Search 를 클릭
- 좌측 메뉴의 "Keys and Endpoint"를 선택
- "Show Keys" 버튼을 클릭하여, 숨김표시된 Key 값을 풀어줌
- Key1 을 복사하여, 아래의
key값에 넣어줌
key = '당신만의 Azure Cognitive Service (Bing Search) API Key를 넣어주세요'
# 아래 한줄의 코드는 일종의 리스트를 만들어줌 (정확히는 Tuple)
disney_characters = 'disney malificent', 'disney cinderella', 'disney jasmin', 'disney mulan', 'disney belle', 'disney pocahontas'
# Path는 fastai에서 개발한 fastcore에 포함된 기초 라이브러리로,
# 기본적으로는 Python에서 표준적으로 제공하는 pathlib.Path를 확장한 것임
# - pathlib.Path의 기능을 모두 그대로 사용 가능하지만,
# - 여기에 몇 가지 편의 사항을 추가함
# - .readlines, .read, .write, .save, .load, .ls
path = Path('disney')
# 최상위 디렉토리의 생성
if not path.exists():
path.mkdir()
# 각 이미지 클래스 별로 반복하여 접근
for character in disney_characters:
# 클래스 이름의 Path 지정 및 생성
dest = (path/character)
dest.mkdir(exist_ok=True)
# search_images_bing 함수를 이용하여, URL 목록을 가져옴
# - Azure Cognitive Service API Key 및 검색하고자 하는 키워드를 입력
# - 이 함수가 반환하는 객체는 Python의 표준 객체인 list를 확장한 L 이라는 객체임 (fastcore)
results = search_images_bing(key, character)
# download_images 함수를 이용하여, 준비된 URL 목록의 모든 이미지를 다운로드함
# - 정확히는 results가 URL 목록은 아니며, Bing Search Service가 반환한 JSON 포맷의 내용임
# - L 객체는 attrgot 이라는 메서드를 제공하는데, 리스트에 포함된 모든 아이템으로부터 인자로 지정된 속성의 값들만을 추출하여, 별도의 리스트(L)을 반환함
# - 첫 번째 인자인 dest가 이미지 다운로드 후 저장될 위치임
download_images(dest, urls=results.attrgot('content_url'))
모든 이미지파일의 Path 목록
fastai에서 제공하는 get_image_files는 지정된 Path를 기점으로, 하위에 포함된 모든 이미지 목록을 재귀적으로 검색하여 들고옴
구분없이 몽땅 들고오는 이유는 다음과 같음
- 다운로드된 이미지는 폴더이름 단위로 클래스가 구분됨
- 이후
DataBlock또는ImageDataLoaders객체 생성시 클래스(레이블)을 구분해내기 위한 로직 추가가 가능함. 구분하는 별도의 함수를 만들게 되며, 단순히 규칙을 지정해 주기만 하면됨.
- 이후
아래 코드의 실행결과는 fnames 객체 내용을 출력해줌
- 출력 결과의 앞 부분 (#...)은 리스트에 포함된 아이템의 개수를 의미함. 원래 표준 list 객체는 이러한 정보를 출력하지 않으나, L은 출력해 주는 특성이 있음.
fnames = get_image_files(path)
fnames
failed = verify_images(fnames)
failed
L 객체에는 함수형 언어적 기능인 map 메서드가 구현되어 있음. map 메서드의 파라미터는 어떤 함수가 지정될 수 있음.
map 메서드가 호출되는 순간, 각 아이템을 반복적으로 접근하면서, 제공된 함수를 각 아이템에 적용하여 반환된 결과를 싸그리 모아서 새로운 L 객체를 만들어줌.
Path.unlink 라는 함수가 하는일은 failed 에 포함된 모든 아이템 (Path)에 대하여, 파일을 삭제하는일을 수행함. Path.unlink는 Python의 표준 라이브러리임.
failed.map(Path.unlink)
다운로드된 이미지는 딱히 Trainining, Validation 으로 구분되어 저장되어 있지 않음
- 경우에 따라서, 데이터셋이 미리 train/valid 와 같은 디렉토리로 나뉘어져 제공되는 경우가 있음.
- 이 때는 splitter에 할당되는 객체의
valid_pct값을 지정하지 않으면 됨. 그러면 자동으로train및valid라는 이름의 디렉토리를 대상으로 삼음. 즉, 다른 이름의 디렉토리라면, 이 이름을train및valid라는 이름으로 맞춰줘야함.
disney_characters = DataBlock(
blocks=(ImageBlock, CategoryBlock),
get_items=get_image_files,
splitter=RandomSplitter(valid_pct=0.2, seed=42),
get_y=parent_label,
item_tfms=Resize(128),
batch_tfms=aug_transforms(mult=2.0, size=224))
dls = disney_characters.dataloaders(path)
dls.show_batch()
learn = cnn_learner(dls, resnet34, metrics=error_rate)
learn.fine_tune(4)
learn.fine_tune(4)
cleaner = ImageClassifierCleaner(learn)
cleaner
count = 0
for idx in cleaner.delete():
cleaner.fns[idx].unlink()
count = count+1
print(count)
interp = ClassificationInterpretation.from_learner(learn)
interp.plot_confusion_matrix()
learn.export()
btn_upload = widgets.FileUpload()
btn_upload
img = PILImage.create(btn_upload.data[-1])
out_pl = widgets.Output()
out_pl.clear_output()
with out_pl: display(img.to_thumb(128,128))
out_pl
learn_inf = load_learner('export.pkl')
pred,pred_idx,probs = learn_inf.predict(img)
lbl_pred = widgets.Label()
lbl_pred.value = f'Prediction: {pred}; Probability: {probs[pred_idx]:.04f}'
lbl_pred
def on_click_classify(change):
img = PILImage.create(btn_upload.data[-1])
out_pl.clear_output()
with out_pl: display(img.to_thumb(256,256))
pred,pred_idx,probs = learn_inf.predict(img)
lbl_pred.value = f'Prediction: {pred}; Probability: {probs[pred_idx]:.04f}'
btn_run = widgets.Button(description='Classify')
btn_run.on_click(on_click_classify)
VBox([widgets.Label('Select your disney character!'),
btn_upload, btn_run, out_pl, lbl_pred])